#include <curl/curl.h>
#include <libxml/tree.h>
#include <libxml/parser.h>
#include <iostream>
#include <vector>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <map>

typedef std::vector<std::string> strings;


static char curlerror[CURL_ERROR_SIZE];

std::string data;

size_t curl_write_callbackf( void *ptr, size_t size, size_t nmemb, void *stream );

bool cmpxmlstr( const xmlChar *a, const std::string &b ) {
   return reinterpret_cast<const char*>(a) == b;
}

const char *xmlChar2char( const xmlChar* c ) {
   return reinterpret_cast<const char*>(c);
}

typedef std::vector< std::pair<std::string,std::string> > item_t;

bool read_item(xmlNode *n, item_t &items, item_t &newitems) {
   using namespace std;
   if( cmpxmlstr(n->name, "item" ) ) {
      string title;
      string link;
      for( xmlNode *n3 = n->children; n3; n3 = n3->next ) {
         if( cmpxmlstr(n3->name, "title") ) {
            for( xmlNode *n4 = n3->children; n4; n4 = n4->next )
               if( n4->type == XML_TEXT_NODE )
                  title += xmlChar2char(n4->content);
         } else if( cmpxmlstr(n3->name, "link" ) ) {
            for( xmlNode *n4 = n3->children; n4; n4 = n4->next )
               if( n4->type == XML_TEXT_NODE )
                  link += xmlChar2char(n4->content);
         }
         if( !title.empty() && !link.empty() ) {
            pair<string,string> p(title,link);
            if( find(items.begin(), items.end(), p ) == items.end() ) {
               items.push_back( p );
               newitems.push_back( p );
            }
            title.clear();
            link.clear();
         }
      }
      return true;
   }
   return false;
}

std::string extract_filename(const std::string &path) {
   std::string::size_type p = path.find_last_of("/");
   if( p != std::string::npos )
      return path.substr(p);
   else
      return path;
}

bool file_exists(const std::string &filename) {
   struct stat s;
   return stat(filename.c_str(),&s) == 0;
}

void download_files(const item_t &items) {
   using namespace std;
   cout << "Trying to download files...\n";
   string download_dir = "/pub/podcasts/x-play/";
   vector<string> files;
   DIR *d = opendir(download_dir.c_str());
   if( d ) {
      dirent *f;
      while( (f = readdir(d)) )
         files.push_back(f->d_name);
      closedir(d);
      for(item_t::const_iterator i = items.begin(); i != items.end(); ++i) {
         string file = extract_filename(i->second);
         string fname = download_dir+file;
         if( file_exists(fname) )
            cout << "Skipping \"" << i->second << "\" - File already exists\n";
         else {
            cout << "Downloading \"" << i->second << "\"\n";
            FILE *fd = fopen(fname.c_str(),"w");
            if( fd ) {
               CURL *c = curl_easy_init();
               curl_easy_setopt(c, CURLOPT_NOPROGRESS, 1);
               curl_easy_setopt(c, CURLOPT_WRITEDATA, fd);
               curl_easy_setopt(c, CURLOPT_WRITEFUNCTION, 0);
               curl_easy_setopt(c, CURLOPT_URL, i->second.c_str());
               curl_easy_setopt(c, CURLOPT_FOLLOWLOCATION, 1);
               curl_easy_perform(c);
               curl_easy_cleanup(c);
               fclose(fd);
            } else
            cout << "File \"" << fname << "\" could not be opened.\n";
         }
      }
   } else
      cerr << "Directory \"" << download_dir << "\" not found.\n";
   cout << "Finished downloading files\n";
}

int main( int argc, char *argv[] ) {
   using namespace std;
   strings args(argv, argv+argc);
   for( strings::const_iterator i = args.begin(); i != args.end(); i++ )
      cout << *i << '\n';
   CURL *curl = curl_easy_init();
   if( !curl ) {
      cerr << "Error initializing libcurl -- exiting without downloading any files.\n";
      return 1;
   }
   curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1);
   curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &curl_write_callbackf);
   curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, &curlerror);
   curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
   item_t items;
   item_t newitems;
   bool first = true;
   for(;;) {
      for( size_t j = 1; j < args.size(); j++ ) {
         newitems.clear();
         data.clear();
         char *url = new char[ args[j].size() ];
         strcpy(url,args[j].c_str());
         curl_easy_setopt(curl, CURLOPT_URL, url);
         curl_easy_perform(curl);
         delete [] url;
         char filename[] = "/tmp/feedreadXXXXXX";
         mkstemp(filename);
         FILE *tf = fopen(filename, "w");
         fprintf(tf, data.c_str());
         fclose(tf);
         xmlDoc *doc = 0;
         xmlNode *root = 0;
         LIBXML_TEST_VERSION
            if( !(doc = xmlParseFile(filename)) ) {
               cerr << "Could not parse XML file\n";
               return 1;
            }
         root = xmlDocGetRootElement(doc);
         string title, link, description;
         for( xmlNode *node = root->children; node; node = node->next ) {
            if( cmpxmlstr(node->name, "channel") ) {
               for( xmlNode *n2 = node->children; n2; n2 = n2->next ) {
                  if( !read_item(n2,items,newitems) ) {
                     /* <title> */
                     if( cmpxmlstr(n2->name, "title") ) {
                        for( xmlNode *n3 = n2->children; n3; n3 = n3->next )
                           if( n3->type == XML_TEXT_NODE )
                              title += xmlChar2char(n3->content);
                        /* </title> */
                     } else if( cmpxmlstr(n2->name, "link") ) {
                        /* <link> */
                        for( xmlNode *n3 = n2->children; n3; n3 = n3->next ) {
                           xmlChar2char(n3->name);
                           if( n3->type == XML_TEXT_NODE )
                              link += xmlChar2char(n3->content);
                        }
                        /* </link> */
                     } else if( cmpxmlstr(n2->name, "description") ) {
                        /* <description> */
                        for( xmlNode *n3 = n2->children; n3; n3 = n3->next )
                           if( n3->type == XML_TEXT_NODE )
                              description += xmlChar2char(n3->content);
                        /* </description> */
                     } 
                  }

               }
            } else 
               read_item(node,items,newitems);
         }
/*         if( first ) {
            cout << "title: " << title << '\n';
            cout << "link: " << link << '\n';
            cout << "description: " << description << '\n';
            first = false;
         }*/
         xmlFreeDoc(doc);
         xmlCleanupParser();
/*         for( vector< pair<string,string> >::reverse_iterator i = newitems.rbegin();
               i != newitems.rend(); i++ )
            cout << i->first << "\n\tURL: " << i->second <<"\n\n";*/
         /*      for( int i = items.size()-1; i >= current; i-- )
                 cout << items[i].first << "\n\tURL: " << items[i].second <<"\n\n";
                 current = items.size()-1;*/
         remove(filename);
      }
      download_files(items);
      sleep(300);
   }
   curl_easy_cleanup(curl);
}

size_t curl_write_callbackf( void *ptr, size_t size, size_t nmemb, void *) {
   data.reserve(data.size()+size*nmemb);
   for( size_t i = 0; i < size*nmemb; i++ )
      data += static_cast<char*>(ptr)[i];
   return nmemb*size;
}
